/*-----------------------------------------------------------------------------
 * FILE: socket.h
 * AUTH: xuwannian@gmail.com
 * TIME: 2008-04-29
 *---------------------------------------------------------------------------*/
#ifndef __SOCKET_H__
#define __SOCKET_H__

#include <stdio.h>

#ifdef _WIN32
#pragma comment(lib, "ws2_32.lib")
#include <WINSOCK2.H>

#define ERRORCODE	(code = (0 - ::WSAGetLastError()))

#define  INITSOCKET() do {		\
	WORD	wVersionRequested;	\
	WSADATA wsaData;			\
	wVersionRequested = MAKEWORD(2, 0);	\
	if  (0 != ::WSAStartup(wVersionRequested, &wsaData))	\
	{	::WSACleanup(); /*return SOCKET_ERROR; */}			\
	} while (0)
#elif
#define INITSOCKET()
#endif

namespace xuwn {

	enum SELECT_RETURN_CODE { EXCEPTION=-1, TIMEOUT, HASDATA};

//////////////////////////////////////////////////////////////////////////
// class CSocket
class CSocket
{
public:
			 CSocket(){};
	virtual ~CSocket(){};
public:
	static int FdCanRead(SOCKET s, unsigned int sec=1)
	{
		TIMEVAL tv;
		tv.tv_sec	= sec;
		tv.tv_usec	= 0;
		
		fd_set rfd;
		FD_ZERO(&rfd);
		FD_SET(s, &rfd);
		
		return (int)::select(0, &rfd, NULL, NULL, &tv);
	}

	static int FdCanWrite(SOCKET s, unsigned int sec=1)
	{
		TIMEVAL tv;
		tv.tv_sec	= sec;
		tv.tv_usec	= 0;
		
		fd_set rfd;
		FD_ZERO(&rfd);
		FD_SET(s, &rfd);
		
		return (int)::select(0, NULL, &rfd, NULL, &tv);
	}

	const char* GetLocalAddr(char* addr)
	{
		INITSOCKET();
		char name[256];
		memset(name, 0, sizeof(name));
		
		if (0 != gethostname(name, sizeof(name) - 1))
		{
			return NULL;
		}
		
		struct hostent* he = gethostbyname(name);
		
		struct sockaddr_in in;
		
		for (int nAdapter = 0; he->h_addr_list[nAdapter]; nAdapter++) 
		{
			memcpy( &in.sin_addr.s_addr, he->h_addr_list[nAdapter], he->h_length);
			char* p = inet_ntoa((struct in_addr)in.sin_addr);
			
			memcpy(addr, p, strlen(p));
			printf("addr: %s\n", addr);
		}
		
		char* p = inet_ntoa((struct in_addr)in.sin_addr);
		
		memcpy(addr, p, strlen(p));
		
		return (char*)addr;
	}

	virtual BOOL Init(const char* addr, unsigned int port)
	{
		INITSOCKET();
		if (SOCKET_ERROR == (__s = ::socket(AF_INET, SOCK_STREAM, 0)))
		{
			::WSACleanup();
			return ERRORCODE;
		}

		::memset(&__addr_in, 0, sizeof(__addr_in));
		
		__addr_in.sin_family	 	= AF_INET;
		__addr_in.sin_addr.s_addr	= ::inet_addr(addr);
		__addr_in.sin_port			= ::htons(port);

		return TRUE;
	}

	void Close()
	{
		::shutdown(__s		, SD_BOTH);
		::shutdown(__listen	, SD_BOTH);

		::closesocket(__s);
		::closesocket(__listen);
	}
	
	static void Close(SOCKET s)
	{
		::shutdown(s, SD_BOTH);
		::closesocket(s);
	}

	int						code;
protected:
	struct sockaddr_in		__addr_in;
	SOCKET					__s;
	SOCKET					__listen;
	
}; // END class socket

class client : public CSocket
{
public:
			 client()	{}
	virtual ~client()	{}
public:
	virtual BOOL Connect()
	{
		return (SOCKET_ERROR != ::connect(__s, (const sockaddr*)(&__addr_in), sizeof(__addr_in))) ? TRUE : (ERRORCODE);
	}

	SOCKET GetSocket()
	{ return __s; }
}; // END class client

class server : public CSocket
{
public:
			 server()	{}
	virtual ~server()	{}
public:
	virtual BOOL Init(const char* addr, unsigned int port)
	{
		if (!CSocket::Init(addr, port))
			return ERRORCODE;

		return  (SOCKET_ERROR != ::bind(__s, (sockaddr*)&__addr_in, sizeof(__addr_in)) &&
				 SOCKET_ERROR != ::listen(__s, SOMAXCONN)) ?  TRUE : ERRORCODE;

	}

	virtual BOOL HasConnect(unsigned int sec=1)
	{
		TIMEVAL tv;
		
		tv.tv_sec 	= sec;
		tv.tv_usec	= 0;
		
		fd_set rfd;
		FD_ZERO(&rfd);
		FD_SET(__s, &rfd);
		
		if (0 < ::select(0, &rfd, NULL, NULL, &tv))
		{
			::memset(&__addr_in, 0, sizeof(__addr_in));
			
			int nlen = sizeof(__addr_in);
			
			__listen = ::accept(__s, (sockaddr*)&__addr_in, &nlen);
			
			return TRUE;
		}
		else
		{
			return ERRORCODE;
		}
	}

	const char* GetRemoteAddr(char* addr)
	{
		int nlen = sizeof(__addr_in);
		::getpeername(__listen, (struct sockaddr*)&__addr_in, &nlen);
		char* pszhost = ::inet_ntoa(__addr_in.sin_addr);
		::memcpy(addr, pszhost, ::strlen(pszhost));
		return (char*)addr;
	}

	SOCKET GetSocket()
	{ return __listen; }
}; // END class server
}

#endif